package com.qiguliuxing.dts.service.seckill;

import com.qiguliuxing.dts.common.constant.RedisKeyConsts;
import com.qiguliuxing.dts.common.type.BatchDayEnum;
import com.qiguliuxing.dts.common.type.SeckillStatusEnum;
import com.qiguliuxing.dts.core.redis.RedisServiceUtil;
import com.qiguliuxing.dts.db.bean.SeckillGoodsVo;
import com.qiguliuxing.dts.db.dao.DtsGoodsMapper;
import com.qiguliuxing.dts.db.dao.DtsGoodsProductMapper;
import com.qiguliuxing.dts.db.domain.*;
import com.qiguliuxing.dts.service.base.DtsUserSeckillSubService;
import com.qiguliuxing.dts.service.seckill.cache.SeckillCache;
import com.qiguliuxing.dts.service.seckill.cache.work.BatchGoodToCacheWork;
import com.qiguliuxing.dts.service.seckill.cache.work.BatchTimeConfigWork;
import com.qiguliuxing.dts.service.seckill.model.SeckillCacheOrder;
import com.qiguliuxing.dts.service.seckill.model.SeckillStatus;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalTime;
import java.util.*;

/**
 * 秒杀服务
 *
 * @author 1197673878@qq.com
 * @since 2020年10月13日 上午12:00:34
 */
@Service
public class SeckillGoodsService {
    private static final Logger logger = LoggerFactory.getLogger(SeckillGoodsService.class);

    @Resource
    private DtsGoodsProductMapper dtsGoodsProductMapper;
    @Resource
    private DtsGoodsMapper dtsGoodsMapper;

    @Autowired
    private BatchGoodToCacheWork batchGoodToCacheWork;

    @Autowired
    private SeckillCache seckillCache;

    @Autowired
    private BatchTimeConfigWork batchTimeConfigWork;

    @Autowired
    private DtsUserSeckillSubService dtsUserSeckillSubService;


    /**
     * 做秒杀操作
     * @param userId   userId
     * @param productId  productId
     * @return 秒杀状态
     */
    public SeckillStatus doSeckill(int userId, int seckillGoodsId, int productId, int productNum) {
        DtsSeckillGoods seckillGoods = seckillCache.getSeckillGoods(seckillGoodsId);
        if (seckillGoods == null) {
            // 如果未获取到秒杀商品配置，则可能被删除，意外中断
            logger.warn("警告：秒杀商品配置未正常获取,商品可能被临时删除,秒杀意外中断 秒杀商品id：{}！", seckillGoodsId);
            return new SeckillStatus(SeckillStatusEnum.SECKILL_INTERRUPT);
        }
        DtsSeckillBatch seckillBatch = seckillCache.getSeckillBatch(seckillGoods.getBatchId());

        // 优化调整，如果用户存在未支付订单，无论活动是否结束，需根据用户已抢购的未支付订单（缓存中）进行秒杀结果返回
        String seckillOrder = seckillCache.getSeckillOrder(userId , seckillGoodsId);
        if (StringUtils.isNotBlank(seckillOrder)) {
            SeckillCacheOrder cacheOrder = new SeckillCacheOrder(seckillOrder);
            if (!cacheOrder.isHasOrder() && cacheOrder.getProductId() != null && productId == cacheOrder.getProductId().intValue()) {
                return new SeckillStatus(SeckillStatusEnum.SECKILL_GET);
            } else if (!cacheOrder.isHasOrder() && cacheOrder.getProductId() != null && !seckillCache.isSeckillGoodFinish(seckillGoodsId)) {
                // 如果是未提交订单，且切换了货品，在商品为结束秒杀前，需要清理前面的为支付订单和恢复库存
                RedisServiceUtil.hdel(seckillCache.getToCleanedKey(seckillGoodsId), String.valueOf(userId));
                RedisServiceUtil.del(seckillCache.getSeckillOrderKey(userId, seckillGoodsId));
                seckillCache.addStock(cacheOrder.getProductId(), cacheOrder.getProductNum().shortValue());
                logger.warn("用户；{} 抢购秒杀商品id :{} ,换成货品productId:{} 进行抢购,原货品productId {} 秒杀库存恢复库存量：{}!",
                        userId, seckillGoodsId, productId, cacheOrder.getProductId(), cacheOrder.getProductNum());
            }
        }

        // 判断活动是否开始 (商品秒杀开始日期是否已到，当前时间在秒杀批次内, 如果当前时间在批次时间之后，也表示明日的秒杀批次尚未开始）
        LocalTime nowTime = LocalTime.now();
        LocalDate today = LocalDate.now();
        if (today.isBefore(seckillGoods.getStartDay()) || nowTime.isBefore(seckillBatch.getStartTime()) || nowTime.isAfter(seckillBatch.getEndTime()) ) {
            logger.warn("秒杀商品 productId: {}, 活动未开始！", productId);
            return new SeckillStatus(SeckillStatusEnum.UN_STARTED);
        }
        // 判断活动是否结束（秒杀商品最后日期，商品是否已经被抢光而结束）
        if (today.isAfter(seckillGoods.getEndDay()) || seckillCache.isSeckillGoodFinish(seckillGoodsId)) {
            logger.info("秒杀商品 productId: {}, 活动结束！", productId);
            return new SeckillStatus(SeckillStatusEnum.ACT_END);
        }

        // 判断是否有产品库存,判断产品秒杀库存以redis中剩余的库存为准
        int leftNum = seckillCache.getSeckilledProductLeftNum(productId);
        if (leftNum <= 0) {
            logger.warn("秒杀商品 productId: {}, 商品已经销售完成，剩余数量：{}", productId, leftNum);
            return new SeckillStatus(SeckillStatusEnum.SOLD_OUT);
        }

        // 判断是否允许重复秒杀
        boolean allowRepeatSeckill = seckillGoods.getRepeatSeckill();
        if (allowRepeatSeckill) {
            // 如果允许重复秒杀，则需判断剩余库存是否满足抢购数量
            if (leftNum < productNum) {
                logger.warn("秒杀商品 productId: {}, 剩余秒杀库存:{}, 抢购数：{}， 剩余库存不足 ！", productId, leftNum, productNum);
                return new SeckillStatus(SeckillStatusEnum.NOT_ENOUGH);
            }
        } else {
            // 如果不允许重复秒杀，则需判断用户是否已经参与过该商品的秒杀
            if (seckillCache.existSeckillOrder(userId, seckillGoodsId)) {
                logger.warn("用户 userId:{},已经秒杀过商品 seckillGoodsId: {} ！", seckillGoodsId, userId);
                return new SeckillStatus(SeckillStatusEnum.NOT_ALLOW);
            }
        }

        // 秒杀条件已满足，进行秒杀操作
        String randomUUID = UUID.randomUUID().toString().replace("-", "");
        boolean seckillFlag = false;
        if (seckillCache.getSeckillLockKey(RedisKeyConsts.SECKILL_LOCK_KEY, randomUUID)) {
            // 获取了分布式锁，进行秒杀缓存数据的调整
            seckillFlag = seckillCache.handleSeckill(userId, seckillGoodsId, productId, productNum, seckillGoods.getEndDay());
        }
        seckillCache.release(RedisKeyConsts.SECKILL_LOCK_KEY, randomUUID);

        if (seckillFlag) {
            return new SeckillStatus(SeckillStatusEnum.SECKILL_GET);
        } else {
            // 如果秒杀数据调整失败，则直接返回给前端秒杀排队中，用户可重复操作秒杀
            logger.warn("秒杀警告：秒杀用户：{}，货品：{}, 秒杀数据调整失败,返回排队中，用户可重复操作秒杀! ", userId, productId);
            return new SeckillStatus(SeckillStatusEnum.IN_LINE);
        }
    }

    /**
     * 获取某一批次数据
     *
     * @param actIndex 批次ID
     * @param currBatchId 当前批次ID
     * @return
     */
    public List<SeckillGoodsVo> getBatchSeckillGoodByBatchId(Integer userId, int actIndex, int currBatchId) {
        List<SeckillGoodsVo> seckillGoodsVos = new ArrayList<>();

            int status = getSeckillGoodsStatus(actIndex, currBatchId);
            List<DtsSeckillGoods> seckillGoods = batchGoodToCacheWork.getBatch(actIndex);

            seckillGoods.forEach(item -> {
                SeckillGoodsVo seckillGoodsVo = new SeckillGoodsVo();
                BeanUtils.copyProperties(item, seckillGoodsVo);
                // 设置商品状态
                seckillGoodsVo.setSeckillStatus(status);
                try {
                // 计算剩余进度
                calLeftNumPersent(seckillGoodsVo);
                }catch (Exception e){
                    //e.printStackTrace();
                }
                // 设置是否已经提醒
                Map<String, Object> params = new HashMap<>(16);
                params.put("userId", userId);
                params.put("goodsId", item.getId()); // 这里传入的是秒杀商品id
                params.put("timeStatus", 2);
                seckillGoodsVo.setTiped(isSubscribe(params));
                seckillGoodsVos.add(seckillGoodsVo);
            });
        return seckillGoodsVos;
    }

    /**
     * 计算剩余进度
     * @param item
     */
    private void calLeftNumPersent(SeckillGoodsVo item) {
        int leftNum = seckillCache.getSeckilledGoodLeftNum(item.getId());
        int initNum = item.getTotalNumber();
        item.setPercent(getPercent(Double.valueOf(initNum - leftNum), Double.valueOf(initNum)));
        item.setLeftNumber(leftNum);
    }

    public String getPercent(Double x, Double total) {
        double f = (total.intValue() == 0 ? 0D : x * 100 / total);
        BigDecimal bg = new BigDecimal(f);
        double f1 = bg.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
        return (f1 + "%");
    }

    /**
     * 获取是今日批次还是明日批次
     * 根据相关算法处理shouldTime永远在队列头部
     * 你选择的Id(youSelectTimeId)比他大的永远是今天
     * 比他小的永远是明天
     *
     * @param youSelectTimeId 你选择的ID
     * @param shouldTime      应该展示的ID
     * @return
     */
    private int getToDayOrTomorrow(int youSelectTimeId, int shouldTime) {
        return youSelectTimeId >= shouldTime ? BatchDayEnum.TODAY.getDayType() : BatchDayEnum.TOMORROW.getDayType();
    }

    /**
     * 提示相关状态
     *
     * @param youSelectTimeId 你选择批次ID
     * @param currTimeId  当前批次ID
     * @return
     */
    private int getSeckillGoodsStatus(int youSelectTimeId, int currTimeId) {
        int status = SeckillStatusEnum.UN_STARTED.getStatus();
        if (youSelectTimeId == currTimeId) {
            // 进行中
            status = SeckillStatusEnum.IN_LINE.getStatus();
        } else if (youSelectTimeId < currTimeId) {
            // 明日开始
            status = SeckillStatusEnum.TOMORROW_START.getStatus();;
        }
        return status;
    }

    protected List<DtsSeckillBatch> getTimeBatchConfigs() {
        return batchTimeConfigWork.getBatchConfig();
       /* Object object = batchTimeConfigWork.getBatchConfig();
        if (object instanceof ArrayList) {
            return (List<DtsSeckillBatch>) object;
        } else {
            JSONArray jsonArray = (JSONArray) object;
            return JSON.parseArray(jsonArray.toString(), DtsSeckillBatch.class);
        }*/
    }

    /**
     * 根据用户秒杀选择的商品，获取缓存中购物信息
     * @param userId
     * @param seckillGoodsId
     * @return
     */
    public List<DtsCart> getCheckedCartForCache(Integer userId, Integer seckillGoodsId) {
        String seckillOrder = seckillCache.getSeckillOrder(userId, seckillGoodsId);
        SeckillCacheOrder cacheOrder = new SeckillCacheOrder(seckillOrder);
        List<DtsCart> dtsCartList = new ArrayList<>();
        if (cacheOrder.getProductId() != null) {
            DtsCart dtsCart = new DtsCart();
            DtsGoodsProduct product = dtsGoodsProductMapper.selectByPrimaryKey(cacheOrder.getProductId());
            DtsGoods goods = dtsGoodsMapper.selectByPrimaryKey(product.getGoodsId());
            dtsCart.setUserId(userId);
            dtsCart.setBrandId(goods.getBrandId());
            dtsCart.setGoodsId(product.getGoodsId());
            dtsCart.setGoodsSn(goods.getGoodsSn());
            dtsCart.setGoodsName(goods.getName());
            dtsCart.setProductId(product.getId());
            dtsCart.setPrice(product.getSeckillPrice());
            dtsCart.setNumber((short) 1);
            dtsCart.setSpecifications(product.getSpecifications());
            dtsCart.setChecked(true);
            String picUrl = product.getUrl();
            if (StringUtils.isBlank(picUrl)) {
                picUrl = goods.getPicUrl();
            }
            dtsCart.setPicUrl(picUrl);
            dtsCart.setDeleted(false);
            dtsCart.setSettlementMoney(new BigDecimal(0));
            dtsCartList.add(dtsCart);
        }
        return  dtsCartList;
    }

    public int isSubscribe(Map<String, Object> params) {
        Integer subscribed = dtsUserSeckillSubService.selectNotifyExists(params);
        if (subscribed != null && subscribed.intValue() > 0) {
            return 1;
        } else {
            return 0;
        }
    }
}
